home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr47 / tsrsrc34.zip / MARKNET.PAS < prev    next >
Pascal/Delphi Source File  |  1993-04-17  |  20KB  |  710 lines

  1. {**************************************************************************
  2. *   MARKNET - stores system information in a file for later restoration.  *
  3. *   Copyright (c) 1986,1991 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   Version 2.7 3/4/89                                                    *
  7. *     first public release                                                *
  8. *     (based on FMARK 2.6)                                                *
  9. *   Version 2.8 3/10/89                                                   *
  10. *     store the DOS environment                                           *
  11. *     store information about the async ports                             *
  12. *   Version 2.9 5/4/89                                                    *
  13. *     for consistency                                                     *
  14. *   Version 3.0 7/21/91                                                   *
  15. *     for compatibility with DOS 5                                        *
  16. *     add Quiet option                                                    *
  17. *     save BIOS LPT port data areas                                       *
  18. *     save XMS allocation                                                 *
  19. *     add code for tracking high memory                                   *
  20. *   Version 3.1 11/4/91                                                   *
  21. *     no change                                                           *
  22. *   Version 3.2 11/22/91                                                  *
  23. *     change method of accessing high memory                              *
  24. *     store parent's length as well as segment                            *
  25. *   Version 3.3 1/8/92                                                    *
  26. *     new features for parsing and getting command line options           *
  27. *   Version 3.4 2/14/92                                                   *
  28. *     increase heap space to allow bigger FILES=                          *
  29. *     improve error reporting when out of heap space                      *
  30. *     store HMA status                                                    *
  31. ***************************************************************************
  32. *   Telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  33. *   Requires Turbo Pascal 6 to compile.                                   *
  34. ***************************************************************************}
  35.  
  36. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  37. {$M 2048,0,20000}
  38.  
  39. {.$DEFINE Debug}         {Activate for status messages}
  40. {.$DEFINE MeasureStack}  {Activate to measure stack usage}
  41.  
  42. program MarkNet;
  43.  
  44. uses
  45.   Dos,
  46.   MemU,
  47.   Xms,
  48.   Ems;
  49.  
  50. const
  51.   MarkFOpen : Boolean = False;    {True while mark file is open}
  52.   Quiet : Boolean = False;        {Set True to avoid screen output}
  53.  
  54. var
  55.   MarkName : PathStr;             {Name of mark file}
  56.  
  57.   DevicePtr : ^DeviceHeader;      {Pointer to the next device header}
  58.   DeviceSegment : Word;           {Current device segment}
  59.   DeviceOffset : Word;            {Current device offset}
  60.   MarkF : file;                   {Dump file}
  61.   DosPtr : ^DosRec;               {Pointer to internal DOS table}
  62.   CommandSeg : Word;              {PSP segment of primary COMMAND.COM}
  63.   CommandPsp : array[1..$100] of Byte;
  64.   FileTableA : array[1..5] of SftRecPtr;
  65.   FileTableCnt : Word;
  66.   FileRecSize : Word;
  67.   EHandles : Word;                {For tracking EMS allocation}
  68.   EmsPages : ^PageArray;
  69.   XHandles : Word;                {For tracking XMS allocation}
  70.   XmsPages : XmsHandlesPtr;
  71.   HMAStatus : Byte;
  72.   McbG : McbGroup;                {Mcbs allocated as we go resident}
  73.  
  74.   SaveExit : Pointer;
  75.  
  76.   {$IFDEF MeasureStack}
  77.   I : Word;
  78.   {$ENDIF}
  79.  
  80.   procedure ExitHandler; far;
  81.     {-Trap error exits (only)}
  82.   begin
  83.     ExitProc := SaveExit;
  84.     if MarkFOpen then begin
  85.       if IoResult = 0 then ;
  86.       Close(MarkF);
  87.       if IoResult = 0 then ;
  88.       Erase(MarkF);
  89.     end;
  90.     {Turbo will swap back, so undo what we've done already}
  91.     SwapVectors;
  92.   end;
  93.  
  94.   procedure Abort(Msg : String);
  95.     {-Halt in case of error}
  96.   begin
  97.     WriteLn(Msg);
  98.     Halt(1);
  99.   end;
  100.  
  101.   procedure FindDevChain;
  102.     {-Return segment, offset and pointer to NUL device}
  103.   begin
  104.     DosPtr := Ptr(OS(DosList).S, OS(DosList).O-2);
  105.     DevicePtr := @DosPtr^.NullDevice;
  106.     DeviceSegment := OS(DevicePtr).S;
  107.     DeviceOffset := OS(DevicePtr).O;
  108.   end;
  109.  
  110.   procedure CheckWriteError;
  111.     {-Check for errors writing to mark file}
  112.   begin
  113.     if IoResult = 0 then
  114.       Exit;
  115.     Abort('Error writing to '+MarkName);
  116.   end;
  117.  
  118.   procedure SaveStandardInfo;
  119.     {-Save the ID string, the vectors, and so on}
  120.   type
  121.     IDArray = array[1..4] of Char;
  122.   var
  123.     PSeg : Word;
  124.     ID : IDArray;
  125.   begin
  126.     {Write the ID string}
  127.     {$IFDEF Debug}
  128.     WriteLn('Writing mark file ID string');
  129.     {$ENDIF}
  130.     ID := NetMarkID;
  131.     BlockWrite(MarkF, ID, SizeOf(IDArray));
  132.     CheckWriteError;
  133.  
  134.     {Write the start address of the device chain}
  135.     {$IFDEF Debug}
  136.     WriteLn('Writing null device address');
  137.     {$ENDIF}
  138.     BlockWrite(MarkF, DevicePtr, SizeOf(Pointer));
  139.     CheckWriteError;
  140.  
  141.     {Write the vector table}
  142.     {$IFDEF Debug}
  143.     WriteLn('Writing interrupt vector table');
  144.     {$ENDIF}
  145.     BlockWrite(MarkF, Mem[0:0], 1024);
  146.     CheckWriteError;
  147.  
  148.     {Write miscellaneous save areas}
  149.     {$IFDEF Debug}
  150.     WriteLn('Writing EGA save table');
  151.     {$ENDIF}
  152.     BlockWrite(MarkF, Mem[$40:$A8], 8); {EGA save table}
  153.     CheckWriteError;
  154.     {$IFDEF Debug}
  155.     WriteLn('Writing interapplications communication area');
  156.     {$ENDIF}
  157.     BlockWrite(MarkF, Mem[$40:$F0], 16); {Interapplications communication area}
  158.     CheckWriteError;
  159.     {$IFDEF Debug}
  160.     WriteLn('Writing parent PSP segment and length');
  161.     {$ENDIF}
  162.     PSeg := Mem[PrefixSeg:$16];
  163.     BlockWrite(MarkF, PSeg, 2); {Parent's PSP segment}
  164.     BlockWrite(MarkF, Mem[PSeg-1:3], 2); {Parent's PSP's length}
  165.     CheckWriteError;
  166.     {$IFDEF Debug}
  167.     WriteLn('Writing BIOS printer table');
  168.     {$ENDIF}
  169.     BlockWrite(MarkF, Mem[$40:$8], 10); {Printer ports plus #printers}
  170.     CheckWriteError;
  171.  
  172.     {Write EMS information}
  173.     if EMSpresent then begin
  174.       if MaxAvail < 2048 then begin
  175.         WriteLn('Need 2048 bytes for EMS handle table. Have ', MaxAvail);
  176.         Abort('Insufficient memory');
  177.       end;
  178.       GetMem(EmsPages, 2048);
  179.       EHandles := EMSHandles(EmsPages^);
  180.     end else
  181.       EHandles := 0;
  182.     {$IFDEF Debug}
  183.     WriteLn('Writing EMS handle information');
  184.     {$ENDIF}
  185.     BlockWrite(MarkF, EHandles, SizeOf(Word));
  186.     if EHandles <> 0 then
  187.       BlockWrite(MarkF, EmsPages^, SizeOf(HandlePageRecord)*EHandles);
  188.     CheckWriteError;
  189.  
  190.     {Write XMS information}
  191.     if XmsInstalled then begin
  192.       XHandles := GetXmsHandles(XmsPages);
  193.       HMAStatus := AllocateHma($FFFF);
  194.       if HMAStatus = 0 then
  195.         if FreeHma = 0 then ;
  196.     end else begin
  197.       XHandles := 0;
  198.       HMAStatus := $80;
  199.     end;
  200.     {$IFDEF Debug}
  201.     WriteLn('Writing XMS handle and HMA information');
  202.     {$ENDIF}
  203.     BlockWrite(MarkF, XHandles, SizeOf(Word));
  204.     if XHandles <> 0 then
  205.       BlockWrite(MarkF, XmsPages^, SizeOf(XmsHandleRecord)*XHandles);
  206.     BlockWrite(MarkF, HMAStatus, SizeOf(Byte));
  207.     CheckWriteError;
  208.   end;
  209.  
  210.   procedure SaveDevChain;
  211.     {-Save the device driver chain}
  212.   begin
  213.     {$IFDEF Debug}
  214.     WriteLn('Saving device driver chain');
  215.     {$ENDIF}
  216.     while OS(DevicePtr).O <> $FFFF do begin
  217.       BlockWrite(MarkF, DevicePtr^, SizeOf(DeviceHeader));
  218.       CheckWriteError;
  219.       with DevicePtr^ do
  220.         DevicePtr := Ptr(NextHeaderSegment, NextHeaderOffset);
  221.     end;
  222.   end;
  223.  
  224.   procedure BufferFileTable;
  225.     {-Save an image of the system file table}
  226.   var
  227.     S : SftRecPtr;
  228.     Size : Word;
  229.   begin
  230.     with DosPtr^ do begin
  231.       S := FirstSFT;
  232.       FileTableCnt := 0;
  233.       while OS(S).O <> $FFFF do begin
  234.         Inc(FileTableCnt);
  235.         Size := 6+S^.Count*FileRecSize;
  236.         if MaxAvail < Size then begin
  237.           WriteLn('Need ', Size, ' bytes for system file table. Have ', MaxAvail);
  238.           Abort('Insufficient memory');
  239.         end;
  240.         GetMem(FileTableA[FileTableCnt], Size);
  241.         Move(S^, FileTableA[FileTableCnt]^, Size);
  242.         S := S^.Next;
  243.       end;
  244.     end;
  245.   end;
  246.  
  247.   procedure BufferAllocatedMcbs;
  248.     {-Save an array of all allocated Mcbs}
  249.   var
  250.     HiMemSeg : Word;
  251.     M : McbPtr;
  252.  
  253.     procedure AddMcbs;
  254.     var
  255.       Done : Boolean;
  256.     begin
  257.         repeat
  258.           inc(McbG.Count);
  259.           with McbG.Mcbs[McbG.Count] do begin
  260.             mcb := OS(M).S;
  261.             psp := M^.Psp;
  262.           end;
  263.           Done := (M^.Id = 'Z');
  264.           M := Ptr(OS(M).S+M^.Len+1, 0);
  265.         until Done;
  266.     end;
  267.  
  268.   begin
  269.     McbG.Count := 0;
  270.     M := Mcb1;
  271.     AddMcbs;
  272.  
  273.     HiMemSeg := FindHiMemStart;
  274.     if HiMemSeg <> 0 then begin
  275.       M := Ptr(HiMemSeg, 0);
  276.       AddMcbs;
  277.     end;
  278.   end;
  279.  
  280.   procedure SaveDOSTable;
  281.     {-Save the DOS internal variables table}
  282.   var
  283.     DosBase : Pointer;
  284.     Size : Word;
  285.   begin
  286.     {$IFDEF Debug}
  287.     WriteLn('Saving DOS data area at 0050:0000');
  288.     {$ENDIF}
  289.     BlockWrite(MarkF, mem[$50:$0], $200);
  290.     CheckWriteError;
  291.     DosBase := Ptr(OS(DosPtr).S, 0);
  292.     Size := OS(DosPtr^.FirstSFT).O;
  293.     {$IFDEF Debug}
  294.     WriteLn('Saving DOS variables table at ', HexPtr(DosBase));
  295.     {$ENDIF}
  296.     BlockWrite(MarkF, Size, SizeOf(Word));
  297.     BlockWrite(MarkF, DosBase^, Size);
  298.     CheckWriteError;
  299.   end;
  300.  
  301.   procedure SaveFileTable;
  302.     {-Save the state of the file table}
  303.   var
  304.     I : Word;
  305.     Size : Word;
  306.   begin
  307.     {$IFDEF Debug}
  308.     WriteLn('Saving DOS file table at ', HexPtr(DosPtr^.FirstSFT));
  309.     {$ENDIF}
  310.     BlockWrite(MarkF, FileTableCnt, SizeOf(Word));
  311.     for I := 1 to FileTableCnt do begin
  312.       Size := 6+FileTableA[I]^.Count*FileRecSize;
  313.       BlockWrite(MarkF, FileTableA[I]^, Size);
  314.     end;
  315.     CheckWriteError;
  316.   end;
  317.  
  318.   procedure BufferCommandPSP;
  319.     {-Save the PSP of COMMAND.COM}
  320.   var
  321.     PspPtr : Pointer;
  322.   begin
  323.     CommandSeg := MasterCommandSeg;
  324.     PspPtr := Ptr(CommandSeg, 0);
  325.     Move(PspPtr^, CommandPsp, $100);
  326.   end;
  327.  
  328.   procedure SaveCommandPSP;
  329.   begin
  330.     {$IFDEF Debug}
  331.     WriteLn('Saving COMMAND.COM PSP at ', HexW(CommandSeg), ':0000');
  332.     {$ENDIF}
  333.     BlockWrite(MarkF, CommandPsp, $100);
  334.     CheckWriteError;
  335.   end;
  336.  
  337.   procedure SaveCommandPatch;
  338.     {-Restore the patch that NetWare applies to command.com}
  339.   label
  340.     ExitPoint;
  341.   const
  342.     Patch : array[0..14] of Char = ':/'#0'_______.___'#0;
  343.   var
  344.     Segm : Word;
  345.     Ofst : Word;
  346.     Indx : Word;
  347.   begin
  348.     for Segm := CommandSeg to PrefixSeg do
  349.       for Ofst := 0 to 15 do begin
  350.         Indx := 0;
  351.         while (Indx <= 14) and (Patch[Indx] = Char(Mem[Segm:Ofst+Indx])) do
  352.           Inc(Indx);
  353.         if Indx > 14 then begin
  354.           {$IFDEF Debug}
  355.           WriteLn('Saving COMMAND patch address at ', HexW(Segm), ':', HexW(Ofst));
  356.           {$ENDIF}
  357.           goto ExitPoint;
  358.         end;
  359.       end;
  360.     Segm := 0;
  361.     Ofst := 0;
  362. ExitPoint:
  363.     BlockWrite(MarkF, Ofst, SizeOf(Word));
  364.     BlockWrite(MarkF, Segm, SizeOf(Word));
  365.     CheckWriteError;
  366.   end;
  367.  
  368.   procedure FindEnv(CommandSeg : Word; var EnvSeg, EnvLen : Word);
  369.     {-Return the segment and length of the master environment}
  370.   var
  371.     Mcb : Word;
  372.   begin
  373.     Mcb := CommandSeg-1;
  374.     EnvSeg := MemW[CommandSeg:$2C];
  375.     if EnvSeg = 0 then
  376.       {Master environment is next block past COMMAND}
  377.       EnvSeg := Commandseg+MemW[Mcb:3]+1;
  378.     EnvLen := MemW[(EnvSeg-1):3] shl 4;
  379.   end;
  380.  
  381.   procedure SaveDosEnvironment;
  382.     {-Save the master copy of the DOS environment}
  383.   var
  384.     EnvSeg : Word;
  385.     EnvLen : Word;
  386.     P : Pointer;
  387.   begin
  388.     FindEnv(CommandSeg, EnvSeg, EnvLen);
  389.     {$IFDEF Debug}
  390.     WriteLn('Saving master environment, ', EnvLen, ' bytes at ', HexW(EnvSeg), ':0000');
  391.     {$ENDIF}
  392.     P := Ptr(EnvSeg, 0);
  393.     BlockWrite(MarkF, EnvLen, SizeOf(Word));
  394.     BlockWrite(MarkF, P^, EnvLen);
  395.     CheckWriteError;
  396.   end;
  397.  
  398.   procedure SaveCommState;
  399.     {-Save the state of the communications controllers}
  400.   var
  401.     PicMask : Byte;
  402.     Com : Byte;
  403.     LCRSave : Byte;
  404.     Base : Word;
  405.     ComPortBase : array[1..2] of Word absolute $40:0; {Com port base addresses}
  406.  
  407.     procedure SaveReg(Offset : Byte);
  408.       {-Save one communications register}
  409.     var
  410.       Reg : Byte;
  411.     begin
  412.       Reg := Port[Base+Offset];
  413.       BlockWrite(MarkF, Reg, SizeOf(Byte));
  414.       CheckWriteError;
  415.     end;
  416.  
  417.   begin
  418.     {$IFDEF Debug}
  419.     WriteLn('Saving communications environment');
  420.     {$ENDIF}
  421.  
  422.     {Save the 8259 interrupt enable mask}
  423.     PicMask := Port[$21];
  424.     BlockWrite(MarkF, PicMask, SizeOf(Byte));
  425.     CheckWriteError;
  426.  
  427.     for Com := 1 to 2 do begin
  428.       Base := ComPortBase[Com];
  429.  
  430.       {Save the Com port base address}
  431.       BlockWrite(MarkF, Base, SizeOf(Word));
  432.       CheckWriteError;
  433.  
  434.       if Base <> 0 then begin
  435.         {Save the rest of the control state}
  436.         SaveReg(IER);             {Interrupt enable register}
  437.         SaveReg(LCR);             {Line control register}
  438.         SaveReg(MCR);             {Modem control register}
  439.         LCRSave := Port[Base+LCR]; {Save line control register}
  440.         Port[Base+LCR] := LCRSave or $80; {Enable baud rate divisor registers}
  441.         SaveReg(BRL);             {Baud rate divisor low}
  442.         SaveReg(BRH);             {Baud rate divisor high}
  443.         Port[Base+LCR] := LCRSave; {Restore line control register}
  444.       end;
  445.     end;
  446.   end;
  447.  
  448.   procedure SaveAllocatedMcbs;
  449.     {-Save list of allocated memory control blocks}
  450.   begin
  451.     {$IFDEF Debug}
  452.     WriteLn('Saving memory allocation group');
  453.     {$ENDIF}
  454.     {Save the number of Mcbs}
  455.     BlockWrite(MarkF, McbG.Count, SizeOf(Word));
  456.     CheckWriteError;
  457.     {Save the used Mcbs}
  458.     BlockWrite(MarkF, McbG.Mcbs, 2*SizeOf(Word)*McbG.Count);
  459.     CheckWriteError;
  460.   end;
  461.  
  462.   function CompaqDOS30 : Boolean; assembler;
  463.     {-Return true if Compaq DOS 3.0}
  464.   asm
  465.     mov ah,$34
  466.     int $21
  467.     cmp bx,$019C
  468.     mov al,1
  469.     jz @Done
  470.     dec al
  471. @Done:
  472.   end;
  473.  
  474.   procedure ValidateDosVersion;
  475.     {-Assure supported version of DOS and compute size of DOS internal filerec}
  476.   var
  477.     DosVer : Word;
  478.   begin
  479.     DosVer := DosVersion;
  480.     case Hi(DosVer) of
  481.       3 : if (Hi(DosVer) < $0A) and not CompaqDOS30 then
  482.             {IBM DOS 3.0}
  483.             FileRecSize := 56
  484.           else
  485.             {DOS 3.1+ or Compaq DOS 3.0}
  486.             FileRecSize := 53;
  487.       4, 5 : FileRecSize := 59;
  488.     else
  489.       Abort('Requires DOS 3, 4, or 5');
  490.     end;
  491.   end;
  492.  
  493.   procedure SaveIDStrings;
  494.     {-Save identification strings within the PSP}
  495.   var
  496.     ID : String[10];
  497.   begin
  498.     Move(MarkName, Mem[PrefixSeg:$80], Length(MarkName)+1);
  499.     Mem[PrefixSeg:$80+Length(MarkName)+1] := 13;
  500.     ID := NmarkID;
  501.     Move(ID[1], Mem[PrefixSeg:NmarkOffset], Length(ID));
  502.   end;
  503.  
  504.   procedure CloseStandardFiles;
  505.     {-Close all standard files}
  506.   var
  507.     H : Word;
  508.   begin
  509.     for H := 0 to 4 do
  510.       asm
  511.         mov ah,$3E
  512.         mov bx,H
  513.         int $21
  514.       end;
  515.   end;
  516.  
  517.   procedure GetOptions;
  518.     {-Get command line options}
  519.   var
  520.     Arg : String[127];
  521.  
  522.     procedure UnknownOption;
  523.     begin
  524.       WriteLn('Unknown command line option: ', Arg);
  525.       Halt(1);
  526.     end;
  527.  
  528.     procedure BadOption;
  529.     begin
  530.       WriteLn('Invalid command line option: ', Arg);
  531.       Halt(1);
  532.     end;
  533.  
  534.     procedure WriteCopyright;
  535.     begin
  536.       WriteLn('MARKNET ', Version, ', Copyright 1991 TurboPower Software');
  537.     end;
  538.  
  539.     procedure WriteHelp;
  540.     begin
  541.       WriteCopyright;
  542.       WriteLn;
  543.       WriteLn('MARKNET saves a picture of the PC system status in a file,');
  544.       WriteLn('so that the state can later be restored by using RELNET.');
  545.       WriteLn;
  546.       WriteLn('MARKNET accepts the following command line syntax:');
  547.       WriteLn;
  548.       WriteLn('  MARKNET [Options] MarkFile');
  549.       WriteLn;
  550.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  551.       WriteLn('     /Q     write no screen output.');
  552.       WriteLn('     /?     write this help screen.');
  553.       Halt(1);
  554.     end;
  555.  
  556.     procedure GetArgs(S : String);
  557.     var
  558.       SPos : Word;
  559.     begin
  560.       SPos := 1;
  561.       repeat
  562.         Arg := NextArg(S, SPos);
  563.         if Arg = '' then
  564.           Exit;
  565.         if Arg = '?' then
  566.           WriteHelp
  567.         else
  568.           case Arg[1] of
  569.             '-', '/' :
  570.               case Length(Arg) of
  571.                 1 : BadOption;
  572.                 2 : case Upcase(Arg[2]) of
  573.                       '?' : WriteHelp;
  574.                       'Q' : Quiet := True;
  575.                     else
  576.                       BadOption;
  577.                     end;
  578.               else
  579.                 UnknownOption;
  580.               end;
  581.           else
  582.             if Length(MarkName) <> 0 then
  583.               BadOption
  584.             else
  585.               MarkName := StUpcase(Arg);
  586.           end;
  587.       until False;
  588.     end;
  589.  
  590.   begin
  591.     MarkName := '';
  592.  
  593.     {Get arguments from the command line and the environment}
  594.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  595.     GetArgs(GetEnv('MARKNET'));
  596.  
  597.     {Assure mark file specified}
  598.     if Length(MarkName) = 0 then
  599.       WriteHelp;
  600.     if not Quiet then
  601.       WriteCopyright;
  602.   end;
  603.  
  604. begin
  605.   {$IFDEF MeasureStack}
  606.   fillchar(mem[sseg:0], sptr-16, $AA);
  607.   {$ENDIF}
  608.  
  609.   {Must run with standard DOS vectors}
  610.   SwapVectors;
  611.   SaveExit := ExitProc;
  612.   ExitProc := @ExitHandler;
  613.  
  614.   {Get command line options}
  615.   GetOptions;
  616.  
  617.   {Assure supported version of DOS}
  618.   ValidateDosVersion;
  619.  
  620.   {Find the device driver chain and the DOS internal table}
  621.   FindDevChain;
  622.  
  623.   {Save PSP region of COMMAND.COM}
  624.   BufferCommandPSP;
  625.  
  626.   {Buffer the DOS file table}
  627.   BufferFileTable;
  628.  
  629.   {Deallocate environment}
  630.   asm
  631.     mov es,PrefixSeg
  632.     mov es,es:[$002C]
  633.     mov ah,$49
  634.     int $21
  635.   end;
  636.  
  637.   {Buffer the allocated mcb array}
  638.   BufferAllocatedMcbs;
  639.  
  640.   {Open the mark file}
  641.   Assign(MarkF, MarkName);
  642.   Rewrite(MarkF, 1);
  643.   if IoResult <> 0 then
  644.     Abort('Error creating '+MarkName);
  645.   MarkFOpen := True;
  646.  
  647.   {Save ID string, interrupt vectors and other standard state information}
  648.   SaveStandardInfo;
  649.  
  650.   {Save the device driver chain}
  651.   SaveDevChain;
  652.  
  653.   {Save the DOS internal variables table}
  654.   SaveDOSTable;
  655.  
  656.   {Save the DOS internal file management table}
  657.   SaveFileTable;
  658.  
  659.   {Save the PSP of COMMAND.COM}
  660.   SaveCommandPSP;
  661.  
  662.   {Save the location that NetWare may patch in COMMAND.COM}
  663.   SaveCommandPatch;
  664.  
  665.   {Save the master copy of the DOS environment}
  666.   SaveDosEnvironment;
  667.  
  668.   {Save the state of the communications controllers}
  669.   SaveCommState;
  670.  
  671.   {Save list of allocated memory control blocks}
  672.   SaveAllocatedMcbs;
  673.  
  674.   {Close mark file}
  675.   Close(MarkF);
  676.   CheckWriteError;
  677.  
  678.   {Move ID strings into place}
  679.   SaveIDStrings;
  680.  
  681.   if not Quiet then
  682.     WriteLn('Stored mark information in ', MarkName);
  683.  
  684.   {$IFDEF MeasureStack}
  685.   I := 0;
  686.   while I < SPtr-16 do
  687.     if mem[sseg:i] <> $AA then begin
  688.       writeln('unused stack ', i, ' bytes');
  689.       I := SPtr;
  690.     end else
  691.       inc(I);
  692.   {$ENDIF}
  693.  
  694.   Flush(Output);
  695.  
  696.   {Close file handles}
  697.   CloseStandardFiles;
  698.  
  699.   {Go resident}
  700.   asm
  701.     mov dl,byte ptr markname
  702.     xor dh,dh
  703.     add dx,$0090
  704.     mov cl,4
  705.     shr dx,cl
  706.     mov ax,$3100
  707.     int $21
  708.   end;
  709. end.
  710.